﻿using System;
using System.IO;

namespace Transforms.Core
{
    public class NameToLongException : Exception
    {
    }

    public class WrongHeaderException : Exception
    {
    }

    public class UnexpectedFileEnd : Exception
    {
        public UnexpectedFileEnd(string fileName)
            : base(string.Format("file <{0}>", fileName))
        {
        }
    }

    public class FileItem
    {
        public string Name;
        public int Length;
    }

    public class DataProcessor : IDataProcessor
    {
        private string _basis;

        public Descripter DataDescripter { get; private set; }

        public static void DivideData(byte[] data, int size, Func<int, string> nameGiver, Descripter descripter)
        {
            if (nameGiver == null)
                throw new ArgumentNullException("NameGiver");

            if (size == 0)
            {
                var name = nameGiver(1);
                descripter.AddArx(name);
                DataUtils.SaveBytesToFile(name, data);
                return;
            }

            if (size < 50)
                throw new ArgumentException("partSize");

            var position = 0;
            var partIndex = 1;
            while (position < data.Length)
            {
                var arxName = nameGiver(partIndex);
                descripter.AddArx(FileUtils.GetFileName(arxName));
                DataUtils.SaveBytesToFile(arxName, data, position, size);
                position += size;
                partIndex++;
            }
        }

        private static void ExtractData(byte[] data, string destination, Descripter descripter)
        {
            if (!Directory.Exists(destination))
                throw new ArgumentException("destination");

            destination = FileUtils.GetFileDirectoryPath(destination);
            var stream = new MemoryStream(data);
            foreach (var item in descripter.FilesEnumerator())
            {
                var fileBytes = new byte[item.Length];
                var readCount = stream.Read(fileBytes, 0, item.Length);
                if (readCount != item.Length)
                    throw new UnexpectedFileEnd(item.Name);

                var path = destination + item.Name;
                FileUtils.CreateDirectory(FileUtils.GetDirectoryName(path));
                DataUtils.SaveBytesToFile(path, fileBytes);
            }
        }

        public void Compile(DataSourceList list, int partSize, Func<int, string> nameGiver)
        {
            if (nameGiver == null)
                throw new ArgumentNullException("nameGiver");

            var descripter = new Descripter();
            var origin = descripter.LoadSources(list);

            var identity = Identity.Generate();
            var bytes = DataUtils.CompressAndCript(origin, identity);

            DivideData(bytes, partSize, nameGiver, descripter);

            descripter.SaveToFile(nameGiver(0), identity);
        }

        public void ReadHeader(string headerFile, Action<string> addNameAction = null)
        {
            _basis = FileUtils.GetFileDirectoryPath(FileUtils.GetDirectoryName(headerFile));
            DataDescripter = new Descripter(DataUtils.ReadFromFile(headerFile), addNameAction);
        }

        public void Extract(string destination)
        {
            byte[] data;
            using (var stream = new MemoryStream())
            {
                foreach (var file in DataDescripter.GetArxEnumerator())
                    stream.HWrite(DataUtils.ReadFromFile(_basis + file));

                data = stream.ToArray();
            }

            data = DataUtils.DecompressAndDecript(data, DataDescripter.OutIdentity);
            ExtractData(data, destination, DataDescripter);
        }
    }
}
